iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0

我們在最一開始的時候,就已經看過最基本用來管理state的hook-useState了,今天再來看看另一個也是用在管理state的hook,就是useReducer。「useReducer」的用途類似useState,一樣也是進行狀態管理的hook,雖然在vue框架中,並沒有提供這樣的用法,但是和Vuex這類的狀態管理工具的模式有點類似。不過今天就不看Vue了,而是會專注在React的useReducer的用法。

回顧一下useState怎麼用

正式提到useReducer之前,我們先快速透過一個實際的例子來看useState的使用方式。這個例子是「畫面中有+1和-1的按鈕,可以對count增加一和減少一」。

如果是透過setState來進行的話,會需要寫兩個函式來進行加一和減一的操作。這只是一個很簡單的例子,在大多數情況下,幾乎都會利用useState去實作它。

import { useState } from 'react';

function App() {
  const [count, setCount] = useState(0);
  const increment = () => {
    setCount(count + 1);
  };
  const decrement = () => {
    setCount(count - 1);
  };
  return (
    <div className="App">
      <div>
        <h2>Counter</h2>
        <p>current count: {count}</p>
        <div className='buttons'>
          <button onClick={increment}>+1</button>
          <button onClick={decrement}>-1</button>
        </div>
      </div>
    </div>
  );
}

export default App;

useReducer怎麼使用?

前面快速回顧useState後,這裡再來看看如果是useReducer的話,上面同樣的例子會變成怎麼寫呢?想要使用useReducer,通常會有以下三個步驟:
1. 定義reducer function
reducer function會定義對state操作的action,在這個例子裡的action也就是加一減一。在這個reducer function裡面會帶入兩個參數,分別是stateaction

// 在這個例子中,就是如果動作是增加,就把count state +1,如果是減少,就把count state -1。
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

2. 使用useReducer宣告state和dispatch
在這個步驟中,會需要把剛剛定義好的reducer function和state初始值帶進去useReducer裡面。使用useReducer的時候,和使用useState的時候一樣,會回傳一個陣列,這裡一樣會透過解構的方式,把state和更新state的函式給取出,在useReducer中,這個更新state的函式通常會被稱作是dispatch

https://ithelp.ithome.com.tw/upload/images/20230925/20130914zI0eQVIU1i.png

const [state, dispatch] = useReducer(reducer, { count: 0 });

3. 透過dispatch函式進行state的操作
在使用useReducer進行state的更新時,會需要用useReducer回傳的dispatch函式,另外需要帶上要進行的action,才能進行對應的state操作。在這裡傳入的action通常都是是一個內含type key的物件

 <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>

整個完成後,就可以從畫面來進行state的操作了。

https://i.imgur.com/9SXtmfE.gif

useState可以完成的事情,為什麼還要用useReducer?

這時候可能有些人會有個疑問,那就是前面的例子用useState也可以行得通,而且useState用好好的,為什麼還要使用useReducer呢?可以觀察一下操作state的動作這部分,在程式碼上的差異。

// 使用useState時,定義更新state動作的寫法
const increment = () => {
  setCount(count + 1);
};
const decrement = () => {
  setCount(count - 1);
};
// 使用useReducer時,定義更新state動作的寫法
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

的確在許多情境中,使用useState就可以了,但是如果有仔細觀察的話,應該不難發現剛剛兩種寫法中的差異,在useState的寫法中,想要操作state的function需要額外散著寫,當邏輯一複雜,或是元件中還有其他state存在的時候,針對相關程式碼的管理就會比較費工。這個時候如果改成使用useReducer,就能把跟這個state相關的所有操作都歸納在一起,不僅一目瞭然,如果需要調整的時候,也不用找好幾個地方修改,因為跟這個state相關的action都放在同一個reducer,需要使用時,也能很語意化地指定我要做什麼動作,讓reducer自己去對到符合的state操作。

useState用setState觸發畫面重新渲染,那useReducer呢?

useState之所以可以用來管理與畫面渲染有關的state,是因為它會回傳setState的函式,這個函式能夠提醒React我們正在對state進行改動的動作,才能進一步地比較新的state和舊的state是否有變動,再進到呼叫component function的階段。而useReducer的話,則是透過dispatch函式來通知React我們正在進行state的更動,再來也會經歷比對新舊的state,來決定是否要呼叫component function。

總結

雖然useReducer和useState一樣都是在操作state,但是使用useReducer的方式能讓操作state的動作變得更清楚易懂,如果邏輯的基底有很複雜的內容,那用useReducer就能讓程式碼更好管理,因為狀態邏輯能被抽出來。當然如果是一個很單純的state操作,使用useReducer反而會讓使用變得複雜化。在實務中,還是得依照實際的情境下去選擇要用useState還是useReducer,並沒有哪個比較好,或是哪個比較不好,只有適不適合當下的實作內容。

參考資料

Extracting State Logic into a Reducer


上一篇
【Day 20】用ref讓Uncontrolled元件變可控
下一篇
【Day 22】 深層傳遞state!除了props還有其他方式 - proivde & inject和useContext
系列文
從Vue學React!不只要會用,還要真的懂~30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言